#if !defined(TYPELIB_TYPES_H)
#define TYPELIB_TYPES_H

#include <typelib/operations.h>

#include <stdminmax>
#include <cassert>
#include <cmath>

namespace types {

template <class T>
class point {
public:
    ///allows access both by name and by index
    union {
        struct {
			T x, y;
        };
        T v[2];
    };
	point()
		{ x = y = T(); }
	point(T _x, T _y)
		{ x = _x; y = _y; }
	template <class U> 
	point(const point<U> & p)
		{ x = T(p.x); y = T(p.y); }
	~point()
		{}
	T operator [](size_t n) const
		{ return v[n]; }
	bool operator == (const point<T> & p) const
		{ return x == p.x && y == p.y; }
	bool operator != (const point<T> & p) const
		{ return x != p.x || y != p.y; }
	bool operator < (const point<T> & p) const //lexicographical order
		{ return x < p.x || x == p.x && y < p.y; }
	point<T> & operator = (const point<T> & p) 
		{ x = T(p.x); y = T(p.y); return * this; }
	point<T> & operator += (const point<T> & p)
		{ x += p.x; y += p.y; return * this; }
	point<T> & operator -= (const point<T> & p)
		{ x -= p.x; y -= p.y; return * this; }
	template <class F> 
	point<T> & operator *= (F a)
		{ x *= a; y *= a; return * this; }
	template <class F> 
	point<T> & operator /= (F a)
		{ x /= a; y /= a; return * this; }
	friend point<T> operator + (const point<T> & x, const point<T> & y)
		{ point<T> t(x); return t += y; }
	friend point<T> operator - (const point<T> & p)
		{ return point<T>(-p.x, -p.y); }
	friend point<T> operator - (const point<T> & x, const point<T> & y)
		{ point<T> t(x); return t -= y; }
};

template <class T, class F> 
point<T> operator * (const point<T> & x, F a)
	{ point<T> t(x); return t *= a; }
template <class T, class F> 
point<T> operator * (F a, const point<T> & x)
	{ point<T> t(x); return t *= a; }
template <class T, class F> 
point<T> operator / (const point<T> & x, F a)
	{ point<T> t(x); return t /= a; }

typedef point<unsigned char> byte_point;
typedef point<short>	short_point;
typedef point<int>		int_point;
typedef point<size_t>	size_point;
typedef point<float>	float_point;
typedef point<double>	double_point;

template <class T>
class smooth_point : public point<T> {
public:
	point<T> t;
public:
	smooth_point(T _x = T(), T _y = T(), point<T> _t = point<T>())
		: point<T>(_x, _y), t(_t) { }
	smooth_point(point<T> _p, point<T> _t)
		: point<T>(_p), t(_t) { }
	template <class U> smooth_point(const point<U> & rhs)
		: point<T>(rhs), t() { }
};


template <class T>
class extent {
public:
	T sx, sy;
public:
	extent()
		{ }
	extent(T _sx, T _sy)
		: sx(_sx), sy(_sy)
		{ }
	template <class U> extent(const extent<U> & e)
		{ sx = T(e.sx); sy = T(e.sy); }
	template <class U> extent & operator = (const extent<U> & e)
		{ sx = T(e.sx); sy = T(e.sy); }
	bool operator == (const extent<T> & e) const
		{ return sx == e.sx && sy == e.sy; }
	bool operator != (const extent<T> & e) const
		{ return sx != e.sx || sy != e.sy; }
	bool operator < (const extent<T> & e) const
		{ return sx < e.sx || sy < e.sy; }
};

typedef extent<unsigned char> byte_extent;
typedef extent<short>	short_extent;
typedef extent<int>		int_extent;
typedef extent<size_t>	size_extent;
typedef extent<float>	float_extent;
typedef extent<double>	double_extent;


template <class T>
class rect {
public:
	T x1, y1, x2, y2;
public:
	rect()
		{}
	rect(T _x1, T _y1, T _x2, T _y2)
		: x1(_x1), y1(_y1), x2(_x2), y2(_y2)
		{}
	rect(point<T> p1, point<T> p2)
		: x1(p1.x), y1(p1.y), x2(p2.x), y2(p2.y)
		{}
	rect(point<T> p1, extent<T> s)
		: x1(p1.x), y1(p1.y), x2(p1.x + s.sx), y2(p1.y + s.sy)
		{}
	template <class U> rect(const rect<U> & r)
		{ x1 = T(r.x1); y1 = T(r.y1); x2 = T(r.x2); y2 = T(r.y2); }
	template <class U> rect & operator = (const rect<U> & r)
		{ x1 = T(r.x1); y1 = T(r.y1); x2 = T(r.x2); y2 = T(r.y2); return * this; }
	T width() const
		{ return x2 - x1; }
	T height() const
		{ return y2 - y1; }
	extent<T> size() const
		{ return extent<T>(width(), height()); }
	template <class U> bool inside(const point<U> & p) const
		{ return x1 <= p.x && y1 <= p.y && p.x < x2 && p.y < y2; }
	template <class U> bool inside(const rect<U> & r) const
		{ return x1 <= r.x1 && y1 <= r.y1 && r.x2 <= x2 && r.y2 <= y2; }
	template <class U> void limit_inclusive(point<U> & p) const {
		assert(!empty());
		if (p.x < x1) p.x = x1;
		if (p.y < y1) p.y = y1;
		if (p.x > x2) p.x = x2;
		if (p.y > y2) p.y = y2;
	}
	template <class U> void limit_exclusive(point<U> & p) const {
		assert(!empty());
		if (p.x < x1) p.x = x1;
		if (p.y < y1) p.y = y1;
		if (p.x >= x2) p.x = x2-1;
		if (p.y >= y2) p.y = y2-1;
	}
	bool empty() const
		{ return (x1 >= x2) || (y1 >= y2); }
	void clear()
		{ x1 = y1 = x2 = y2 = 0; }
	template <class U> void include(const point<U> & p)
		{ x1 = std::min<T>(x1, p.x);
		  y1 = std::min<T>(y1, p.y);
		  x2 = std::max<T>(x2, p.x+1);
		  y2 = std::max<T>(y2, p.y+1); }
	template <class U> void include2(const point<U> & p)
		{ x1 = std::min<T>(x1, p.x);
		  y1 = std::min<T>(y1, p.y);
		  x2 = std::max<T>(x2, p.x);
		  y2 = std::max<T>(y2, p.y); }
	void inflate(T dx, T dy) {
		x1 -= dx; x2 += dx;
		y1 -= dy; y2 += dx;
	}
	point<T> p1() const
		{ return point<T>(x1, y1); }
	point<T> p2() const
		{ return point<T>(x2, y2); }
	T area() const
		{ return (x2-x1) * (y2-y1); }

	friend bool operator == (const rect<T> & lhs, const rect<T> & rhs)
		{ return lhs.x1 == rhs.x1 && lhs.x2 == rhs.x2 &&
				 lhs.y1 == rhs.y1 && lhs.y2 == rhs.y2; }
	friend bool operator != (const rect<T> & lhs, const rect<T> & rhs)
		{ return !(lhs == rhs); }
};

typedef rect<unsigned char> byte_rect;
typedef rect<short>		short_rect;
typedef rect<int>		int_rect;
typedef rect<size_t>	size_rect;
typedef rect<float>		float_rect;
typedef rect<double>	double_rect;

} // types

using namespace types;

//see in "operations.h" why not-classes are moved outside the namespace

template <class T> 
double scalar_product(const point<T> & p1, const point<T> & p2)
	{ return double(p1.x * p2.x + p1.y * p2.y); }

template <class T> 
double outer_product(const point<T> & p1, const point<T> & p2)
	{ return double(p1.x * p2.y - p1.y * p2.x); }

template <> 
inline double abs(const point<float> & p)
	{ return _hypot(p.x, p.y); } 

template <> 
inline double abs(const point<double> & p)
	{ return _hypot(p.x, p.y); }

template <class T> 
inline void inverse(point<T> & p)
	{ inverse(p.x); inverse(p.y); }

template <class T> 
inline point<T> normalize(const point<T> & p) { 
	double denom = abs(p);
	return denom == 0 ? p : p / denom;
}

template <class T> 
inline double cos(const point<T> & p1, const point<T> & p2) {
	double denom = abs(p1) * abs(p2);
	if (denom == 0)
		return 1.0;
	else {
		double c = scalar_product(p1, p2) / denom;
		return (c > 1.0 ? 1.0 : (c < - 1.0 ? - 1.0 : c));
	}
}

template <class T>
inline double angle(const point<T> & p1, const point<T> & p2) {
	double c = cos(p1, p2);
	if (outer_product(p1, p2) > 0)
		return acos(c);
	else
		return - acos(c);
}

template <class T>
inline point<T> rotate(const point<T> & p, double a)
{ 
	double cosa = cos(a), sina = sin(a);
	return point<T>(T(p.x * cosa - p.y * sina), T(p.x * sina + p.y * cosa)); 
}

template <class T>
inline point<T> perpendicular(const point<T> & p) {
	return point<T>(p.y, - p.x);
}

///projects point 'p' on the line defined by the points pair ('s1','s2')
template <class T>
inline point<T> projection(const point<T> &p, const point<T> &s1, const point<T> &s2)
{
	point<T> d = s2 - s1;
	d *= 1.0 / abs(d);
	point<T> dp = p - s1;
	return scalar_product(d, dp) * d + s1;
}

template <class T>
inline bool intersect(const rect<T> & r1, const rect<T> & r2, rect<T> * r = 0) {
	bool result = 
		(r1.x1 < r2.x2) && (r2.x1 < r1.x2) &&
		(r1.y1 < r2.y2) && (r2.y1 < r1.y2);
	if (result && r != 0) {
		r->x1 = std::max(r1.x1, r2.x1);
		r->x2 = std::min(r1.x2, r2.x2);
		r->y1 = std::max(r1.y1, r2.y1);
		r->y2 = std::min(r1.y2, r2.y2);
	}
	return result;
}

template <class T>
inline void unionrect(const rect<T> & r1, const rect<T> & r2, rect<T> & r) { 
	if (!r1.empty() && !r2.empty()) {
		r.x1 = std::min(r1.x1, r2.x1);
		r.y1 = std::min(r1.y1, r2.y1);
		r.x2 = std::max(r1.x2, r2.x2);
		r.y2 = std::max(r1.y2, r2.y2);
	} else
		if (r1.empty())
			r = r2;
		else
			if (r2.empty())
				r = r1;
}

#endif // TYPELIB_TYPES_H
